package net.wicp.tams.commons.binlog.impl;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

import lombok.extern.slf4j.Slf4j;
import net.wicp.tams.commons.Result;
import net.wicp.tams.commons.apiext.ByteUtil;
import net.wicp.tams.commons.beans.Host;
import net.wicp.tams.commons.binlog.IBinlogRead;
import net.wicp.tams.commons.binlog.IEventRead;
import net.wicp.tams.commons.binlog.bean.EventBean;
import net.wicp.tams.commons.binlog.bean.EventHeader;
import net.wicp.tams.commons.binlog.constant.BinLogVersion;
import net.wicp.tams.commons.binlog.constant.EventFlag;
import net.wicp.tams.commons.binlog.constant.EventType;
import net.wicp.tams.commons.binlog.event.AbsEvent;
import net.wicp.tams.commons.binlog.event.FormatDescription;
import net.wicp.tams.commons.io.InputStreamRamdomRead;

/***
 * 读日志通过文件
 * 
 * @author zhoujunhui
 *
 */
@Slf4j
public class BinglogReadByFile extends InputStreamRamdomRead implements IBinlogRead {
	private EventBean firstEvent;
	private String fileName;

	public BinglogReadByFile(File file) throws IOException {
		super(file);
		fileName=file.getName();
		Host host = AbsEvent.hosts.get();
		if (host == null) {
			host = Host.builder().hostIp("127.0.0.1").port(fileName.hashCode()).build();// 模拟一个host
			AbsEvent.hosts.set(host);
		}
	}

	@Override
	public Result checkHead() {
		try {
			final byte[] magic = this.readBytes(BINLOG_MAGIC.length);
			if (!Arrays.equals(magic, BINLOG_MAGIC)) {
				return Result.getError(String.format("不合法的binlog文件:[%s]", oriFile.getName()));
			} else {
				firstEvent = readEvent();
				BinLogVersion binLogVersion = getVersion(firstEvent.getHead());
				if (binLogVersion == null || binLogVersion != BinLogVersion.v4) {
					return Result.getError(String.format("只支持v4的binlog，不支持的binlog版本:[%s]",
							binLogVersion == null ? "" : binLogVersion.name()));
				}
				return Result.getSuc();
			}
		} catch (Exception e) {
			return Result.getError(String.format("读binlog文件:[%s]错误", oriFile.getName()));
		}
	}

	// 需要在第9个位开始读,读13位
	public BinLogVersion getVersion(EventHeader firstHead) {
		if (firstHead == null) {
			return null;
		}
		BinLogVersion retobj = null;
		switch (firstHead.getEventType()) {
		case FORMAT_DESCRIPTION_EVENT:
			retobj = BinLogVersion.v4;
			break;
		case START_EVENT_V3:
			long eventSize = firstHead.getEventSize();
			if (eventSize == (13 + 56)) {
				return BinLogVersion.v1;
			} else if (eventSize == (19 + 56)) {
				return BinLogVersion.v3;
			}
			break;
		default:
			break;
		}
		return retobj;
	}

	@Override
	public void read(long pos) throws IOException {
		Result checkRet = checkHead();
		if (!checkRet.isSuc()) {
			throw new RuntimeException(checkRet.getMessage());
		}
		// 初始化FormatDescription信息 firstEventHeader
		IEventRead reader = new FormatDescription(firstEvent);
		reader.parseBody();
		if (pos > firstEvent.getHead().getNextEventPos()) {
			this.seek(pos);
		}
		while (true) {
			EventBean curEvent = readEvent();
			if (curEvent == null) {
				break;
			}
			Result ret = curEvent.getEventRead().parseBody();
			if (ret != null) {
				log.info(ret.getMessage());
			}

		}

	}

	@Override
	public EventBean readEvent() throws IOException {
		if (this.available() <= 0) {
			return null;
		}
		long timestamp = ByteUtil.readLongL(readBytes(4));
		EventType eventType = EventType.get(readByte());
		long serverId = ByteUtil.readLongL(readBytes(4));
		long eventSize = ByteUtil.readLongL(readBytes(4));
		long nextEventPos = ByteUtil.readLongL(readBytes(4));
		EventFlag eventFlag = EventFlag.get(ByteUtil.readIntL(readBytes(2)));
		byte[] body = readBytes((int) eventSize - 19);
		EventHeader head = EventHeader.builder().timestamp(timestamp).eventType(eventType).serverId(serverId)
				.eventSize(eventSize).nextEventPos(nextEventPos).eventFlag(eventFlag).build();
		EventBean retobj = new EventBean(head, body);

		log.info(
				"---------------------------事件[{}]开始位置：{}，数据开始位置{}----------------------------------------------------",
				eventType == null ? "" : eventType.name(), AbsEvent.row(retobj.getBeginHead()),
				AbsEvent.row(retobj.getBeginBody()));
		return retobj;
	}

}
